#ifndef XG_JSON_CPP
#define XG_JSON_CPP
////////////////////////////////////////////////////////////////////
#include "../json.h"

static char* cJSON_strdup(const char* str)
{
	int len = str ? strlen(str) : 0;
    char* dest = (char*)cJSON_malloc(len + 1);

    if (len > 0) memcpy(dest, str, len);

	dest[len] = 0;

    return dest;
}
static bool cJSON_load(cJSON* item, const char* str)
{
	cJSON* tmp = cJSON_Parse(str);

	if (tmp == NULL) return false;

	if (item->child) cJSON_Delete(item->child);
	if (item->valuestring) cJSON_free(item->valuestring);

	item->type = tmp->type;
	item->child = tmp->child;
	item->valueint = tmp->valueint;
	item->valuestring = tmp->valuestring;
	item->valuedouble = tmp->valuedouble;

	tmp->child = NULL;
	tmp->valuestring = NULL;

	cJSON_Delete(tmp);

	return true;
}

JsonElement::iterator::iterator(cJSON* item, JsonElement* parent)
{
	idx = -1;
	len = -1;

	if (elem = item)
	{
		if (elem->type == cJSON_Array)
		{
			if ((len = cJSON_GetArraySize(elem)) > 0) idx = 0;
		}
		else if (elem->child)
		{
			idx = 0;
			elem = elem->child;

			while (elem->prev) elem = elem->prev;
		}
	}

	if (parent) holder = parent->holder;
}

JsonElement::iterator& JsonElement::iterator::operator ++ ()
{
	++idx;

	if (len < 0)
	{
		if ((elem = elem->next) == NULL) idx = -1;
	}
	else
	{
		if (idx >= len) idx = -1;
	}

	return *this;
}
JsonElement JsonElement::iterator::operator * ()
{
	cJSON* item = elem;

	if (len >= 0) item = cJSON_GetArrayItem(elem, idx);

	JsonElement res(item, NULL);

	res.holder = holder;

	return res;
}
bool JsonElement::iterator::operator != (const iterator& obj) const
{
	return idx != obj.idx;
}

bool JsonElement::InitElement(cJSON* item)
{
	CHECK_FALSE_RETURN(item);
	
	assert(item->child == NULL);

	if (item->valuestring)
	{
		cJSON_free(item->valuestring);
		item->valuestring = NULL;
	}

	return true;
}
bool JsonElement::InitElement(cJSON* item, int val)
{
	CHECK_FALSE_RETURN(InitElement(item));
	
	item->type = cJSON_Number;
	item->valuedouble = val;
	item->valueint = val;
	
	return true;
}
bool JsonElement::InitElement(cJSON* item, bool val)
{
	CHECK_FALSE_RETURN(InitElement(item));
	
	item->type = val ? cJSON_True : cJSON_False;
	
	return true;
}
bool JsonElement::InitElement(cJSON* item, long val)
{
	return val < INT_MIN || val > INT_MAX ? InitElement(item, stdx::str(val).c_str()) : InitElement(item, (int)(val));
}
bool JsonElement::InitElement(cJSON* item, double val)
{
	CHECK_FALSE_RETURN(InitElement(item));
	
	item->type = cJSON_Number;
	item->valueint = (int)(val);
	item->valuedouble = val;

	return true;
}
bool JsonElement::InitElement(cJSON* item, const char* val)
{
	CHECK_FALSE_RETURN(InitElement(item));
	
	item->type = cJSON_String;
	item->valuestring = cJSON_strdup(val);
	
	return item->valuestring ? true : false;
}
string JsonElement::toString() const
{
	if (elem == NULL) return stdx::EmptyString();

	char* tmp = cJSON_PrintUnformatted(elem);

	if (tmp == NULL) return stdx::EmptyString();

	string res = tmp;

	cJSON_free(tmp);

	return std::move(res);
}
bool JsonElement::init(const string& msg)
{
	if (elem) return load(msg);

	elem = msg.empty() ? cJSON_CreateObject() : cJSON_Parse(msg.c_str());

	if (elem) holder = sp<cJSON>(elem, cJSON_Delete);

	return true;
}
JsonElement JsonElement::add()
{
	if (elem == NULL) init();

	cJSON* item = cJSON_CreateObject();

	if (item) cJSON_AddItemToArray(elem, item);

	return pack(item);
}
void JsonElement::remove(int idx)
{
	cJSON_DeleteItemFromArray(elem, idx);
}
bool JsonElement::load(const string& msg)
{
	if (elem)
	{
		if (elem->valuestring)
		{
			cJSON_free(elem->valuestring);
			elem->valuestring = NULL;
		}

		if (elem->child)
		{
			cJSON_Delete(elem->child);
			elem->child = NULL;
		}

		if (msg.empty())
		{
			elem->type = cJSON_NULL;
			holder = NULL;
			elem = NULL;

			return true;
		}
	}
	else
	{
		if (msg.empty()) return true;

		CHECK_FALSE_RETURN(init());
	}

	return cJSON_load(elem, msg.c_str());
}
void JsonElement::remove(const string& name)
{
	cJSON_DeleteItemFromObject(elem, name.c_str());
}
JsonElement JsonElement::get(int idx) const
{
	if (elem == NULL) return pack(NULL);

	if (idx < 0 || idx >= cJSON_GetArraySize(elem)) return pack(NULL);

	return pack(cJSON_GetArrayItem(elem, idx));
}
JsonElement JsonElement::add(const string& name)
{
	if (elem == NULL)
	{
		init();
	}
	else
	{
		JsonElement tmp = get(name);

		if (tmp.elem) return tmp;
	}

	cJSON* item = cJSON_CreateNull();

	if (item) cJSON_AddItemToObject(elem, name.c_str(), item);

	return pack(item);
}
JsonElement JsonElement::addArray(const string& name)
{
	if (elem == NULL)
	{
		init();
	}
	else
	{
		JsonElement tmp = get(name);

		if (tmp.elem) return tmp;
	}

	cJSON* item = cJSON_CreateArray();

	if (item) cJSON_AddItemToObject(elem, name.c_str(), item);

	return pack(item);
}
JsonElement JsonElement::get(const string& name) const
{
	if (elem == NULL) return pack(NULL);

	return pack(cJSON_GetObjectItem(elem, name.c_str()));
}
int JsonElement::size() const
{
	if (isArray()) return cJSON_GetArraySize(elem);
	
	if (elem->child)
	{
		cJSON* child = elem->child;

		if (child)
		{
			int cnt = 1;

			while (child = child->prev) cnt++;

			child = elem->child;

			while (child = child->next) cnt++;

			return cnt;
		}
	}

	return 0;
}
JsonElement JsonElement::operator [] (int idx)
{
	JsonElement item = get(idx);

	if (item.elem) return item;

	if (elem == NULL) init();

	int sz = size();
	
	if (sz == 0)
	{
		assert(isArray() || elem->child == NULL);
	
		if (elem->valuestring)
		{
			cJSON_free(elem->valuestring);
			elem->valuestring = NULL;
		}

		elem->type = cJSON_Array;
	}
	
	if (isArray())
	{
		while (sz++ <= idx) item = add();
	}
	
	return item;
}
JsonElement JsonElement::operator [] (const string& name)
{
	JsonElement item = get(name);
	
	return item.elem ? item : add(name);
}

string JsonReflect::toString() const
{
	const char* type;
	JsonElement data;
	vector<ReflectItem> vec = ReflectHelper::GetAttrList(this);

	for (auto& item : vec)
	{
		type = item.type;

		if (strcmp(type, "object") == 0)
		{
			JsonReflect* obj = (JsonReflect*)((char*)(this) + item.offset);

			data[item.name].load(obj->toString());
		}
		else
		{
			string val = item.get(this);

			if (strcmp(type, "string") == 0)
			{
				data[item.name] = val;
			}
			else
			{
				if (val.empty()) continue;

				if (strcmp(type, "int") == 0)
				{
					data[item.name] = stdx::atoi(val.c_str());
				}
				else if (strcmp(type, "long") == 0)
				{
					data[item.name] = (long)stdx::atol(val.c_str());
				}
				else if (strcmp(type, "bool") == 0)
				{
					data[item.name] = strcasecmp(val.c_str(), "true") == 0;
				}
				else
				{
					data[item.name] = stdx::atof(val.c_str());
				}
			}
		}
	}

	return data.toString();
}
string JsonReflect::toDocString() const
{
	string doc;
	vector<ReflectItem> vec = ReflectHelper::GetAttrList(this);

	for (auto& item : vec)
	{
		const char* type = item.type;
		const char* remark = item.getRemark();
		const char* extdata = item.getExtdata();

		if (strcmp(type, "object") == 0)
		{
			JsonReflect* obj = (JsonReflect*)((char*)(this) + item.offset);

			type = strstr(obj->getClassName(), "JsonReflectList") ? "array" : "object";
	
			doc += stdx::format("\n<tr><td><span>%s</span></td><td>%s</td><td>%s</td><td>%s</td></tr>", item.name, type, extdata, remark);
			doc += stdx::replace(obj->toDocString(), "\n<tr><td>", "\n<tr><td>&nbsp;&nbsp;<span>&gt;</span>");
		}
		else
		{
			doc += stdx::format("\n<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>", item.name, type, extdata, remark);
		}
	}

	while (true)
	{
		string tmp = stdx::replace(doc, "<span>&gt;</span>&nbsp;&nbsp;", "<span>&nbsp;</span>&nbsp;&nbsp;");

		if (tmp.length() == doc.length()) return doc;

		std::swap(tmp, doc);
	}

	return doc;
}
bool JsonReflect::fromString(const string& msg)
{
	const char* type;
	JsonElement data(msg);

	if (data.isNull()) return true;

	vector<ReflectItem> vec = ReflectHelper::GetAttrList(this);

	CHECK_FALSE_RETURN(data.isObject());

	for (auto& item : vec)
	{
		JsonElement src = data.get(item.name);

		if (src.isNull()) continue;

		type = item.type;

		if (strcmp(type, "object") == 0)
		{
			JsonReflect* obj = (JsonReflect*)((char*)(this) + item.offset);

			CHECK_FALSE_RETURN(obj->fromString(src.toString()));
		}
		else
		{
			string val = src.getVariable();

			if (strcmp(type, "string") == 0)
			{
				CHECK_FALSE_RETURN(item.set(this, val));
			}
			else
			{
				if (val.length() > 0) CHECK_FALSE_RETURN(item.set(this, val));
			}
		}
	}

	return true;
}
bool JsonReflect::fromObject(const JsonReflect& obj)
{
	const char* type;
	const char* name;
	vector<ReflectItem> vec = ReflectHelper::GetAttrList(this);
	vector<ReflectItem> attrs = ReflectHelper::GetAttrList(&obj);

	for (auto& item : vec)
	{
		type = item.type;
		name = item.name;

		if (strcmp(type, "object") == 0)
		{
			JsonReflect* dest = dynamic_cast<JsonReflect*>((Object*)((char*)(this) + item.offset));

			if (dest)
			{
				for (auto& item : attrs)
				{
					if (strcmp(name, item.name) == 0 && strcmp(type, item.type) == 0)
					{
						JsonReflect* src = dynamic_cast<JsonReflect*>((Object*)((char*)(&obj) + item.offset));

						if (src) CHECK_FALSE_RETURN(dest->fromObject(*src));

						break;
					}
				}
			}
		}
		else
		{
			for (auto& item : attrs)
			{
				if (strcmp(name, item.name) == 0)
				{
					string val = item.get(&obj);

					if (strcmp(type, "string") == 0)
					{
						CHECK_FALSE_RETURN(item.set(this, val));
					}
					else
					{
						if (val.length() > 0) CHECK_FALSE_RETURN(item.set(this, val));
					}

					break;
				}
			}
		}
	}

	return true;
}
////////////////////////////////////////////////////////////////////
#endif